
#pragma once

#include "..\\ExternalTools\\ExternalTools.h"

#include "..\\Loirey\\loirey_GLOBAL.h"
#include "..\\Loirey\\loirey_MyMath.h"
#include "..\\Loirey\\loirey_BasicStructure.h"

using namespace loirey;

template<typename TypeOfGradient>
class CImageGradient
{
public:
	const static unsigned int USING_MASK_XY1x3 = 1;
protected:
	bool _fAreMasksInited;
	CKernel1D<TypeOfGradient> _MSK_1x3;
public:
	int W,H,WH;
	CImg<TypeOfGradient> MagnitudeImage;
	CImg<TypeOfGradient> OrientationImage;
	CImg<TypeOfGradient> xGradientImage;
	CImg<TypeOfGradient> yGradientImage;
	TypeOfGradient OriMin, OriMax;

protected:
	void _InitMasks()
	{
		if (_fAreMasksInited) return;

		_MSK_1x3.InitByCenter(1);
		_MSK_1x3.data[0] = -1;
		_MSK_1x3.data[1] = 0;
		_MSK_1x3.data[2] = +1;

		_fAreMasksInited = true;
	}
public:
	void myRelease()
	{
	}
	CImageGradient()
	{
		_fAreMasksInited = false;
		_InitMasks();

		W = H = WH = 0;
		myRelease();
	}
	~CImageGradient()
	{
		myRelease();
	}
	template<typename TypeOfImage>
	void InitGradient(const CImg<TypeOfImage>& SourceImage, unsigned int ComputingKernel, bool fUseSignedOrientation, int z = 0)
	{
		if (!_fAreMasksInited) _InitMasks();

		int _Z,_V;
		int v,i;
		TypeOfGradient* pM;
		TypeOfGradient* pO;
		TypeOfGradient* pX;
		TypeOfGradient* pY;
		TypeOfGradient mag;
		TypeOfGradient ori;

		W = SourceImage.dimx();
		H = SourceImage.dimy();
		WH = W*H;
		_Z = SourceImage.dimz();
		_V = SourceImage.dimv();

		MagnitudeImage.assign(W,H,1,1);
		OrientationImage.assign(W,H,1,1);
		xGradientImage.assign(W,H,1,1);
		yGradientImage.assign(W,H,1,1);
		if (ComputingKernel == USING_MASK_XY1x3)
		{
			for ( v = 0; v < _V; ++v )
			{
				pM = MagnitudeImage.data;
				pO = OrientationImage.data;
				pX = xGradientImage.data;
				pY = yGradientImage.data;
				_MSK_1x3.ConvolveX(W,H,pX,SourceImage.data+WH*(z+v*_Z));
				_MSK_1x3.ConvolveY(W,H,pY,SourceImage.data+WH*(z+v*_Z));
				for ( i = 0; i < WH; ++i )
				{
					mag = (TypeOfGradient)sqrt(CMyMath::sqr(*pY) + CMyMath::sqr(*pX));
					ori = (TypeOfGradient)atan2(*(pY++),*(pX++));
					if (!v || mag>*pM)
					{
						*pM = mag;
						*pO = ori;
					}
					++pM; ++pO;
				}
			}
			pM = MagnitudeImage.data;
			pO = OrientationImage.data;
			pX = xGradientImage.data;
			pY = yGradientImage.data;
			for ( i = 0; i < WH; ++i )
			{
				*(pX++) = *pM * cos(*pO);
				*(pY++) = *pM * sin(*pO);
				++pM; ++pO;
			}
			OriMin = +PI;
			OriMax = -PI;
		}
		else
		{
			//TODO
		}
		// Processing Orietation
		if (!fUseSignedOrientation)
		{
			pO = OrientationImage.data;
			for ( i = 0; i < WH; ++i )
			{
				if (*pO<0) *pO += PI;
				++pO;
			}
			OriMin = 0;
			OriMax = +PI;
		}
	}
};

